import java.util.ArrayList;
import java.util.Random;
public class PotentialTeam8_Player implements MancalaPlayer
{

        private class Node
        {
                public ArrayList<Node> children;
                public MancalaGameState gs;
                public int heuristic_value;
                public int move;
                public int player;
                public Node parent = null;


                
                public Node(MancalaGameState gs)
                {
                        this.gs = gs;
                        children = new ArrayList<Node>();
                        heuristic_value = 0;
                        player = gs.CurrentPlayer;

                }

                public void generateChildren() throws Exception
                {
                        MancalaGameState gs_copy = gs.copy();
                        if(player==0){
                                for(int i=0;i<gs.cols();i++)
                            {
                                    if(gs.validMove(i))
                                    {
                                            gs_copy.play(i);
                                            Node child = new Node(gs_copy);
                                            child.move = i; 
                                            child.parent = this;
                                            children.add(child);
                                    }

                                    gs_copy = gs.copy();
                            }
                        }
                        else{
                                for(int i=gs.cols()-1;i>=0;i--)
                            {
                                    if(gs.validMove(i))
                                    {
                                            gs_copy.play(i);
                                            Node child = new Node(gs_copy);
                                            child.move = i; 
                                            child.parent = this;
                                            children.add(child);
                                    }

                                    gs_copy = gs.copy();
                            }
                        }
       
                }
                


                public void addChild(MancalaGameState gs)
                {
                        Node temp = new Node(gs);
                        children.add(temp);
                }

                public ArrayList<Node> getChildren()
                {
                        if(children.size()<=0)
                        {
                                System.out.println("Empty List");
                                return children;
                        }
                        else
                        {
                                return children;
                        }
                }
                
                public String toString(){
                        String s ="";
                        for(Node child: children){
                                s+="The move "+ child.move+" has heurstic value of "+child.heuristic_value+". \n";
                        }
                        return s;
                }

                                public void computeParentValue(int value) {
                                        parent.heuristic_value = Math.min(parent.heuristic_value,value);
                                        
                                }
                
                                public void computeMinParentValue(int value) {
                                    parent.heuristic_value = Math.min(parent.heuristic_value,value);
                                    
                            }
                                public void computeMaxParentValue(int value) {
                                    parent.heuristic_value = Math.max(parent.heuristic_value,value);
                                    
                            }

        }

        int player;
        
        private int score;
        private WeightCollection myWeights;
        public int gamesPlayed;
        
        
        /*
         * temp[0] = 21;
                temp[1] = 6;
                temp[2] = 77;
                temp[3] = 21;
                temp[4] = 59;
         * 
         */
        
        public PotentialTeam8_Player(int player_num)
        {
        	player = player_num;
            int[] temp = new int[5];
            temp[0] = 4;
            temp[1] = 3;
            temp[2] = 83;
            temp[3] = 10;
            temp[4] = 64;
            myWeights = new WeightCollection(temp);
        }
        
        public PotentialTeam8_Player(int player_num, WeightCollection weights)
        {
                player = player_num;
                myWeights = weights;
                score = 0;
                gamesPlayed = 0;
        }

        public int alphaBeta(Node gameNode, int depth, int a, int b, int player) throws Exception
        {
                //System.out.println("The game node in depth of "+depth+" with move of "+gameNode.move);
                int alpha = a;
                int beta = b;
                for(Node child : gameNode.children)
                {
                      child.heuristic_value = evaluate(child.gs);
                        child.generateChildren();
                }
                
                if(gameNode.gs.checkEndGame()||depth==0){
                        gameNode.heuristic_value = evaluate(gameNode.gs);
                        //gameNode.computeMinimaxForParent(gameNode.heuristic_value);
                        return gameNode.heuristic_value;
                }
                
                if(player == this.player)
                {
                        for(Node child : gameNode.children)
                        {
                                
                                alpha = Math.max(alpha, alphaBeta(child,depth-1,alpha,beta,child.player));
                                child.heuristic_value = alpha;
                               // child.computeParentValue(child.heuristic_value);
                                if(beta<=alpha)
                                {
                                //      System.out.println("Beta Cut-Off");
                                        break;
                                }
                        //System.out.println("Beta of depth "+depth+": ");
                        //child.gs.printState();

                        }
                      //  gameNode.heuristic_value = alpha;
                return alpha;
                }
                else
                {
                        for(Node child : gameNode.children)
                        {
                                beta= Math.min(beta, alphaBeta(child,depth-1,alpha,beta,child.player));
                                child.heuristic_value = beta;
                               // child.computeParentValue(child.heuristic_value);
                                if(beta<=alpha)
                                {
                                //      System.out.println("Alpha Cut-Off");
                                        break;
                                }
                                //System.out.println("Alpha of depth "+depth+": ");
                        //      child.gs.printState();
                                
                        }
                      //  gameNode.heuristic_value = beta;
                        return beta;
                }
    }


        public int evaluate(MancalaGameState gs)
        {
        	 MancalaGameState gs_copy = gs.copy();
             int sd = ScoreDifference(gs_copy);
             int sic = StoneInControl(gs_copy);
             int matc = MaxAmountToCapture(gs_copy);
             int maltc = MaxAmountLoseToCapture(gs_copy);
             
                if(gs_copy.checkEndGame()){
                    gs_copy.computeFinalScore();
                        if (gs_copy.getScore(player) > gs_copy.getScore(1-player)) {
                            return 1000;   
                        }
                        else
                         return -1000;
                   }

                if(myWeights == null)
                {
                        //preset weights

                        return  (4*sd+(sic*3)+(matc*5)-(maltc*6));
                }
                else
                {
                        /* Needs to be replaced with heuristic corresponding to weights! */
                        return  (myWeights.getWeight(0)*sd+(sic*myWeights.getWeight(1))+(matc*myWeights.getWeight(2))-(maltc*myWeights.getWeight(3)));
                }
        }       
        
        private int MaxAmountLoseToCapture(MancalaGameState gs) {
                int max = 0;

                for(int i = 0; i < 6; i++)
                {
                        
                        if(gs.stonesAt(1-player, i) == 0 && gs.stonesAt(player, i)>0 )
                        {
                                
                                int myStone = gs.stonesAt(player, i);
                                int move = findReachableMoveToBucket(gs,i,1-player);
                                if(move!=-1)
                                {
                                        if(myStone > max)
                                        {       
                                                max = myStone;
                                        }
                                        /* use to debug whether it finds a buckt that can be captured
                                        System.out.println("WARNING: There is an opening of "+myStone+" stones to lose at bucket "+i+" with move "+move);
                                        gs.printState();
                                        */
                                }
                                        
                        }
                }
                
                return max;
        }

        private int MaxAmountToCapture(MancalaGameState gs) {
                int max = 0;

                for(int i = 0; i < 6; i++)
                {
                        
                        if(gs.stonesAt(player, i) == 0 && gs.stonesAt(1-player, i)>0 )
                        {
                                
                                int opponentStone = gs.stonesAt(1-player, i);
                                int move = findReachableMoveToBucket(gs,i,player);
                                if(move!=-1)
                                {
                                        if(opponentStone > max)
                                        {       
                                                max = opponentStone;
                                        }
                                        /* use to debug whether it finds a buckt that can be captured
                                        System.out.println("There is an opening of "+opponentStone+" stones to capture at bucket "+i+" with move "+move);
                                        gs.printState();
                                        */
                                }
                                        
                        }
                }
                
                return max;
        }

        private int findReachableMoveToBucket(MancalaGameState gs, int bucket, int player) {
                for(int i=0;i<gs.cols();i++)
                {
                        int stone = gs.stonesAt(player, i);
                        if(gs.validMove(i)&&player==0){
                                if(i<bucket){
                                        if((bucket-i)==stone){
                                                return i;
                                        }
                                }
                                if(i>bucket){
                                        if((13-stone)==(bucket-i)){
                                                return i;
                                        }
                                }
                        }
                        if(gs.validMove(i)&&player==1){
                                if(i>bucket){
                                        if((bucket-i)==stone){
                                                return i;
                                        }
                                }
                                if(i<bucket){
                                        if((13-stone)==(bucket-i)){
                                                return i;
                                        }
                                }
                        }
                }
                return -1;
        }

        public int ScoreDifference(MancalaGameState gs)
        {
                return  gs.getScore(player)-gs.getScore(1-player);
        }
        
        public int StoneInControl(MancalaGameState gs)
        {
                int number = 0;
                
                for(int i=0;i<gs.cols();i++)
                {
                        number+= gs.stonesAt(player,i)-gs.stonesAt(1-player,i);
                }
                return number;
        }
        
        
        public void findFreeMoves(Node root){
                for(Node child: root.children){
                        int stones = root.gs.stonesAt(root.player, child.move);
                        if(root.player==0){
                                if(stones==(root.gs.cols()-child.move)%13){
                                        if(myWeights!=null){
                                                child.heuristic_value += myWeights.getWeight(4)*3;
                                        }
                                        else{
                                                
                                                        child.heuristic_value +=30;
                                                
                                        }
                                        
                                }
                                
                        }
                        else{
                                if(stones==(child.move+1)%13){
                                        if(myWeights!=null){
                                                child.heuristic_value += myWeights.getWeight(4)*3;
                                        }
                                        else{
                                                
                                                        child.heuristic_value +=30;
                                                
                                        }
                                }
                        }
                }

        }

        //auto generated method by implementing MancalaPlayer
        //returns the move the computer selected
        @Override
        public int getMove(MancalaGameState gs) throws Exception {
                // TODO Auto-generated method stub
                Node root = new Node(gs);
                root.generateChildren();
                
                if(gs.validMove(-1)){
                        if(gs.getScore(1-player)>=2){
                                return -1;
                        }
                }

                alphaBeta(root,4,Integer.MIN_VALUE,Integer.MAX_VALUE,player);
                findFreeMoves(root); 
                System.out.println(root.toString());
                int max = Integer.MIN_VALUE;
                int move = -1;
                for(Node child : root.children){
                        if(child.heuristic_value>=max){
                                max = child.heuristic_value;
                                move = child.move;
                        }
                }

                 
                  return move;   // Tell the game what our final decision is.
        }

        
        //performs post game actions.
        @Override
        public Object postGameActions(MancalaGameState gs) {
                // TODO Auto-generated method stub
                return null;
        }

        public int getScore() { return score; }
        public void resetScore() { score = 0;}
        public void addWin() {score = score +2;}
        public void addDraw() {score = score +1;}
        
        public WeightCollection getWeights() {return myWeights;}
        public WeightCollection returnMyWeights(){return myWeights;}
                @Override
                public Object actionsBeforeDeletion() {
                        // TODO Auto-generated method stub
                        return null;
                }
        
                

}
